package com.cloudinnov.task;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cloudinnov.dao.ControlSolutionConfigDao;
import com.cloudinnov.dao.EquipmentPointsDao;
import com.cloudinnov.dao.EquipmentsAttrDao;
import com.cloudinnov.logic.ControlSolutionLogic;
import com.cloudinnov.model.ControlSolutionConfigFire;
import com.cloudinnov.model.ControlSolutionExpre;
import com.cloudinnov.model.ControlSolutionExpression;
import com.cloudinnov.model.EquipmentPoints;
import com.cloudinnov.model.EquipmentsAttr;
import com.cloudinnov.model.FireCRTEvent;
import com.cloudinnov.utils.CommonUtils;
import com.cloudinnov.utils.support.spring.SpringUtils;
import com.cloudinnov.websocket.ControlSolutionDialogueWebsocket;
import com.googlecode.aviator.AviatorEvaluator;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class SchduleScanJob {

	private static final Logger logger = LoggerFactory.getLogger(SchduleScanJob.class);
	private ScheduledThreadPoolExecutor exec;
	ControlSolutionLogic controlSolutionLogic = SpringUtils.getBean("controlSolutionLogic");
	ControlSolutionConfigDao controlSolutionConfigDao = SpringUtils.getBean("controlSolutionConfigDao");
	EquipmentsAttrDao equipmentsAttrDao = SpringUtils.getBean("equipmentsAttrDao");
	EquipmentPointsDao equipmentPointsDao = SpringUtils.getBean("equipmentPointsDao");
	JedisPool jedisPool = SpringUtils.getBean("jedisPool");

	private static final String EXPRESS_HEAD = "expresstest:"; // 有恢复方案,存redis的key的开始
	private static final String EXPIRE_HEAD = "expiretest:"; // 没有恢复方案,存redis的key的开始
	private static final String POSITION_WARNING = "posiotionWarning:"; // 出现严重以上等级的存redis的key的开始
	private static final String EXPRESS_FOOT = ":value";
	private static final String EXPRESS_DOT = ",";

	private static final String CONTROL_SOLUTION_FLAG = "controlSolutionFlag";

	private static final Integer StartEffectFlag = 0;// 开始方案标识
	private static final Integer EndEffectFlag = 1;// 结束方案标识
	private List<ControlSolutionExpre> tmpRecoverExpression;

	public SchduleScanJob() {
		// 初始化中间存储恢复方案
		tmpRecoverExpression = new ArrayList<>();
	}

	public void execute() {
		exec = new ScheduledThreadPoolExecutor(1);
		exec.scheduleAtFixedRate(new SchedultTask(), 2000, 5000, TimeUnit.MILLISECONDS);
	}

	/**
	 * 定时任务线程runnable
	 * 
	 * @author Administrator
	 *
	 */
	class SchedultTask implements Runnable {

		private Jedis redis;

		@Override
		public void run() {
			logger.info("群控方案扫描启动");
			try {
				redis = jedisPool.getResource();
				// 触发方案轮询
				List<ControlSolutionExpre> selectScheduleExpressions = null;
				if (CommonUtils.ControlSolutionCacheMap == null) {
					CommonUtils.ControlSolutionCacheMap = new HashMap<>();
					selectScheduleExpressions = controlSolutionLogic.selectScheduleExpression(0);
					CommonUtils.ControlSolutionCacheMap.put(CONTROL_SOLUTION_FLAG, selectScheduleExpressions);
				} else {
					selectScheduleExpressions = (List<ControlSolutionExpre>) CommonUtils.ControlSolutionCacheMap
							.get(CONTROL_SOLUTION_FLAG);
				}
				for (int i = 0; i < selectScheduleExpressions.size(); i++) {
					long startMills = System.currentTimeMillis();
					startSingleJob(selectScheduleExpressions.get(i), redis);
					long endMills = System.currentTimeMillis();
					long time = (endMills - startMills);
					logger.info("单个触发方案扫描周期：" + time + "毫秒");
				}

				// 恢复方案轮询
				Set<String> solutionCodeKeys = redis.keys(EXPRESS_HEAD + "*");
				if (solutionCodeKeys.size() > 0) {
					for (String solutionCodeKey : solutionCodeKeys) {
						String solutionCode = solutionCodeKey.split(":")[1];
						ControlSolutionExpre controlSolutionExpressionRecover = controlSolutionLogic
								.selectControlSolutionExpreByRecover(solutionCode, 1);
						if (controlSolutionExpressionRecover != null) {
							tmpRecoverExpression.add(controlSolutionExpressionRecover);
						}
					}

					if (tmpRecoverExpression.size() > 0) {
						for (int i = 0; i < tmpRecoverExpression.size(); i++) {
							long startMills = System.currentTimeMillis();
							startSingleJobRecover(tmpRecoverExpression.get(i), redis);
							long endMills = System.currentTimeMillis();
							long time = (endMills - startMills);
							logger.info("单个恢复方案扫描周期：" + time + "毫秒");
						}
					}
				}
			} catch (Exception e) {
				logger.error(e.toString());
			} finally {
				jedisPool.returnResource(redis);
			}
			logger.info("群控方案扫描结束");
			tmpRecoverExpression.clear();
		}
	}

	/**
	 * 根据表达式结果 触发控制
	 * 
	 * @param controlSolutionExpression
	 *            方案表达式
	 * @param redis
	 *            redis参数
	 */
	public void startSingleJob(ControlSolutionExpre controlSolutionExpression, Jedis redis) {
		// 调用方法,获取所有表达式的string类型值
		String generateExpression = generateExpression(controlSolutionExpression, redis);
		boolean judgeExpressionResult = judgeExpressionResult(generateExpression);

		System.err.println("触发方案结果:  " + generateExpression + "  ||result:" + judgeExpressionResult);
		if (judgeExpressionResult) {
			// 获取灾难的控制级别
			Integer level = controlSolutionExpression.getLevel();
			String solutionCode = controlSolutionExpression.getSolutionCode();
			// 此处需要判断灾难的级别,普通的直接触发控制,其他级别的往前端弹出警告框,确认后再触发
			if (level == 1 || level == 2) {
				// 在redis中获取有恢复方案或无恢复方案的情况:有,跳过;没有,继续执行
				if (redis.get(EXPRESS_HEAD + solutionCode + EXPRESS_FOOT) == null
						&& redis.get(EXPIRE_HEAD + solutionCode + EXPRESS_FOOT) == null) {
					logger.info("直接触发群控方案!");
					// 调用群控设备方法,控制设备
					startControlSolutionControl(controlSolutionExpression, redis, level);
				}
			} else {
				logger.info("弹出警告框的代码被触发!");
				// 判断最终的表达式结果是true,对该群控的所有设备进行判断定位是哪个设备出问题,用来往前端传值弹警告框
				judgePositionOfAlarm(controlSolutionExpression, redis, level);
			}
		}
	}

	/**
	 * 根据表达式结果 触发恢复控制
	 * 
	 * @param controlSolutionExpression
	 *            方案表达式
	 * @param redis
	 *            redis参数
	 */
	public void startSingleJobRecover(ControlSolutionExpre controlSolutionExpression, Jedis redis) {

		String generateExpression = generateExpression(controlSolutionExpression, redis);
		boolean judgeExpressionResult = judgeExpressionResult(generateExpression);
		System.err.println("恢复方案结果:  " + generateExpression + "|| result:" + judgeExpressionResult);
		if (judgeExpressionResult) {
			logger.info("恢复方案群控触发!");
			endControlSolutionControl(controlSolutionExpression, redis);
		}
	}

	/**
	 * 组建条件表达式,整合成谷歌插件需要的表达式形式
	 * 
	 * @param expression
	 * @param mapValue
	 * @return
	 */
	public String generateExpression(ControlSolutionExpre controlSolutionExpression, Jedis redis) {
		String expression = controlSolutionExpression.getCondition();
		Map<String, String> mapValue = new HashMap<String, String>();
		String pointCodes = controlSolutionExpression.getPointCode();
		String[] pointCodeArray = pointCodes.split(",");
		for (String pointCode : pointCodeArray) {
			String detectorValue;
			if ("datetime".equals(pointCode)) {
				detectorValue = getNowDateTime();
			} else {
				// 获取redis中对应的值
				detectorValue = getRedisValue(pointCode, redis);
			}
			mapValue.put("$" + pointCode, detectorValue);
		}
		return expressionFormat(expression, mapValue);
	}

	/**
	 * 判断出现灾难位置的方法并发websocket
	 * 
	 * @param controlSolutionExpression
	 * @param redis
	 */
	public void judgePositionOfAlarm(ControlSolutionExpre controlSolutionExpression, Jedis redis, Integer level) {
		//封装condition和从redis获取的值,用来把code换成redis取回的数值
		Map<String, String> mapValue = new HashMap<String, String>();
		boolean result = false;
		String conditions = controlSolutionExpression.getCondition();
		//获取调接口用的code值,ControlSolutionExpre的code值
		String code = controlSolutionExpression.getCode();
		String pointCode = null;
		//对conditions表达式进行处理
		conditions = conditions.replaceAll("&&", ",");
		conditions = conditions.replaceAll("\\|", ",");
		conditions = conditions.replaceAll("\\(", ",");
		conditions = conditions.replaceAll("\\)", ",");
		//去除多个','号存在的情况,只剩下一个
		while (conditions.contains(",,")) {
			conditions = conditions.replaceAll(",,", ",");
		}
		//去除掉条件中以','开始或以','结尾
		if (conditions.startsWith(",")) {
			conditions = conditions.substring(1);
		}
		if (conditions.endsWith(",")) {
			conditions = conditions.substring(0,conditions.lastIndexOf(","));
		}
		String[] conditionArray = conditions.split(",");
		for (String condition : conditionArray) {
			if (!condition.contains("datetime") && condition != null) {
 				String detectorValue = null;
 				pointCode = condition.substring(condition.indexOf("$")+1, condition.indexOf("$")+1+8);
				detectorValue = getRedisValue(pointCode, redis);
				mapValue.put("$" + pointCode, detectorValue);
				String expressionFormat = expressionFormat(condition, mapValue);
				//判断字符串的表达式的值是true还是false
				result = judgeExpressionResult(expressionFormat);
 			}
			//对表达式的结果进行分类判断
			if (result) {
				String redisSolutionCode = controlSolutionExpression.getSolutionCode();
				//判断该设备出现异常,从redis中查询,没有,第一次出现,继续下面操作;有,跳过
				if (redis.get(POSITION_WARNING + redisSolutionCode) == null) {
					redis.set(POSITION_WARNING + redisSolutionCode, redisSolutionCode + "设备:异常");
					//设置失效时间
					redis.expire(POSITION_WARNING + redisSolutionCode, 1800);
					
					//调用接口方法获取异常点的信息,被修改的地方
					FireCRTEvent fireCRTEvent = controlSolutionLogic.selectFireCRTEventByCode(code, pointCode, 0);
					String event = fireCRTEvent.getEvent(); //category_name:报警设备所属分类
					String categoryCode = fireCRTEvent.getCategoryCode(); //报警设备所属分类的code值
					
					String equipmentCode = fireCRTEvent.getEquipmentCode(); //报警设备的唯一标示:equipmentCode
					String device = fireCRTEvent.getDevice(); //solution_name:警告信息名称
					String position = fireCRTEvent.getPosition();  //pile_no:报警位置
					String solutionCode = fireCRTEvent.getSolutionCode(); //群控方案唯一标示:solutionCode
					
					//创建查询条件的类
					ControlSolutionConfigFire controlSolutionConfigFire = new ControlSolutionConfigFire();
					controlSolutionConfigFire.setSolutionCode(solutionCode);
					controlSolutionConfigFire.setIsRecover(0);
					List<ControlSolutionConfigFire> cscs = controlSolutionConfigDao.selectListByConfigFireRecover(controlSolutionConfigFire);
					List<ControlSolutionConfigFire.Config> configs;
					for (ControlSolutionConfigFire model : cscs) {
						configs = new ArrayList<>();
						if(model.getEquipmentClassify().equals("CDZSQ")){
							EquipmentPoints equipPoint = equipmentPointsDao.selectCDZSQIsLeftByEquipmentCode(model.getEquipmentCode());
							if(equipPoint != null && equipPoint.getIndexType() != null) {
								model.setSelectTrue(true);
							}else {
								model.setSelectTrue(false);
							}
						}
						String config = model.getTextConfig();
						if (CommonUtils.isNotEmpty(config)) {
							configs.addAll(JSON.parseArray(config, ControlSolutionConfigFire.Config.class));
						}
						HashMap<String, Object> map = new HashMap<String, Object>();
						String equipCode = model.getCode();// 获取设备编码
						List<EquipmentsAttr> attrList = equipmentsAttrDao.selectListByEquipmentCode(equipCode); // 根据设备编码查询设备属性
						for (EquipmentsAttr equipmentsAttr : attrList) {
							if (equipmentsAttr.getCustomTag() != null && equipmentsAttr.getCustomTag() != "") {
								map.put(equipmentsAttr.getCustomTag(), equipmentsAttr.getValue());
							}
						}
						model.setConfigs(map);
						model.setConfig(configs);
						model.setEquipmentCode(model.getCode());
 	 				}
					
					//转换为json
					String cscJson = JSON.toJSONString(cscs);
					
					/*	
				 	//去除json中的""
					String cscJson1 = cscJson.replace('"', ' ');
					//去除转义符 \
					String cscJson2 = cscJson1.replace('\\', ' ');
					System.err.println("cscJson2: " + cscJson2);
					*/
					
					// 调用msg2Map方法,封装警告框数据
					JSONObject objectJson = msg2JSON(event, categoryCode, equipmentCode, device, position, solutionCode, level);
					JSONObject toWeb = new JSONObject();
					toWeb.put("data", objectJson);
					toWeb.put("list", cscs); //直接存
					
					// 调用socket的方法往前端发送警告框数据
					sendMesssage(toWeb); 	//触发群控的操作已有
					
					//当level等于3或4时,调用该方法
					startControlSolutionControl(controlSolutionExpression, redis, level);
				}
			}
		}
	}

	/**
	 * 对触发群控的方案 加入redis中作为恢复检测依据, 当接触报警时 触发
	 * 
	 * @param expression
	 * @param solutionCode
	 */
	public void startControlSolutionControl(ControlSolutionExpre controlSolutionExpression, Jedis redis,
			Integer level) {
		logger.info("群控方案触发,对redis操作!");
		// 有恢复方案redis的key
		String expressKey = EXPRESS_HEAD + controlSolutionExpression.getSolutionCode() + EXPRESS_FOOT;
		// 没有恢复方案redis的key
		String expireKey = EXPIRE_HEAD + controlSolutionExpression.getSolutionCode() + EXPRESS_FOOT;

		ControlSolutionExpre controlSolutionExpressionRecover = controlSolutionLogic
				.selectControlSolutionExpreByRecover(controlSolutionExpression.getSolutionCode(), 1);
		// 有恢复方案的,走这个流程
		if (controlSolutionExpressionRecover != null) {
			String pointJsonValue = JSON.toJSONString(controlSolutionExpressionRecover);
			redis.set(expressKey, pointJsonValue);
		} else { // 没有恢复方案的,对redis的键设置生命周期:10分钟,键失效,会再次触发群控,弹出警告框
			redis.set(expireKey, controlSolutionExpression.getSolutionCode());
			redis.expire(expireKey, 600);
		}

		// 针对紧急程度为1和2的,直接触发群控
		if (level == 1 || level == 2) {
			// 普通级,直接发送生效控制方案
			sendControlPlan(controlSolutionExpression.getSolutionCode(), StartEffectFlag);
		}
	}

	/**
	 * 群控方案设备 恢复控制
	 */
	public void endControlSolutionControl(ControlSolutionExpre controlSolutionExpression, Jedis redis) {
		logger.info("恢复方案触发群控!");

		// 查询redis中存储出现故障位置的设备code值
		Set<String> solutionCodes = redis.keys(POSITION_WARNING + "*");
		String solutionCode = controlSolutionExpression.getSolutionCode();

		// 获取所有的equipmentCode的值,并结合solutionCode值查询结果,有结果删除redis的报警位置设备记录
		if (solutionCodes.size() > 0 && solutionCodes != null) {
			for (String warningSolutionCode : solutionCodes) {
				String redisSolutionCode = warningSolutionCode.split(":")[1];
				if (redisSolutionCode != null && redisSolutionCode.equals(solutionCode)) {
	 					redis.del(POSITION_WARNING + solutionCode);
	 			}
 			}
		}

		// 删除触发控制时存在redis中的设备信息,并触发群控方案 恢复
		String key = EXPRESS_HEAD + controlSolutionExpression.getSolutionCode() + EXPRESS_FOOT;
		if (redis.get(key) != null) {
			// 删除redis中的记录
			redis.del(key);
			// 触发群控
			sendControlPlan(controlSolutionExpression.getSolutionCode(), EndEffectFlag);
		}
	}

	/**
	 * 根据方案的CODE 控制群控设备
	 * 
	 * @param solutionCode
	 * @return
	 */
	public int sendControlPlan(String solutionCode, Integer isRecover) {
		// return controlSolutionLogic.sendControllerSolution(solutionCode);
		try {
			controlSolutionLogic.sendControllerSolutionRecover(solutionCode, isRecover);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return 1;
	}

	/**
	 * 判断表达式
	 * 
	 * @param expression
	 * @return
	 */
	public boolean judgeExpressionResult(String expression) {

		try {
			return (Boolean) AviatorEvaluator.execute(expression);
		} catch (Exception e) {
			// logger.error("检测表达式异常");
			return false;
		}
	}

	/**
	 * 格式化字符串
	 * 
	 * @param input
	 * @param map
	 * @return
	 */
	public static String expressionFormat(String input, Map<String, String> map) {
		// 遍历map,用value替换掉key
		for (Map.Entry<String, String> entry : map.entrySet()) {
			input = input.replace(entry.getKey(), entry.getValue());
		}
		return input;
	}

	/**
	 * 获取redis字符串值
	 * 
	 * @param code
	 * @param redis
	 * @return
	 */
	public static String getRedisValue(String code, Jedis redis) {
		String key = "point:" + code + ":value";
		String result = redis.lindex(key, 0);
		if (result != null) {
			String[] split = result.split(",");

			return split[1];
		} else {
			return -1 + "";
		}
	}

	/**
	 * 把发往前端的警告信息整合成map集合
	 * 
	 * @param categoryCode:
	 *            设备代号
	 * @param device:
	 *            警报名
	 * @param event:
	 *            引起警报的来源
	 * @param position:
	 *            警报发生地点
	 * @param solutionCode:
	 *            解决方案
	 * @return
	 */
	public JSONObject msg2JSON(String event, String categoryCode, String equipmentCode, String device, String position,
			String solutionCode, Integer level) {
		JSONObject msgMap = new JSONObject();
		// 获取当前系统时间
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String occurrenceTime = sdf.format(new Date());
		// 设备代号
		msgMap.put("event", event);
		// 警报名
		msgMap.put("categoryCode", categoryCode);
		// 警报触发来源
		msgMap.put("equipmentCode", equipmentCode);
		// 表达式的名称
		msgMap.put("device", device);
		//设备编码
		msgMap.put("deviceId", System.currentTimeMillis()+"");
		// 警报发生时间
		msgMap.put("occurrenceTime", occurrenceTime);
		// 警报发生地点
		msgMap.put("position", position);
		// 警报解决方案
		msgMap.put("solutionCode", solutionCode);
		//警报等急
		msgMap.put("level", level);
		return msgMap;
	}

	/**
	 * 调用webSocket的方法,往前端发送数据
	 * 
	 * @param content
	 * @return
	 */
	public void sendMesssage(JSONObject jsonObject) {
		// 遍历所有连接客户工单WebSocket 推送对话
		Iterator<Map.Entry<String, ControlSolutionDialogueWebsocket>> controlSolutionDialogueWebsocket = ControlSolutionDialogueWebsocket.webSocketMap
				.entrySet().iterator();
		while (controlSolutionDialogueWebsocket.hasNext()) {
			Map.Entry<String, ControlSolutionDialogueWebsocket> controlSolutionDialogueEntry = controlSolutionDialogueWebsocket.next();
			try {
				String jsonString = jsonObject.toJSONString();
				System.err.println("jsonString:" + jsonString);
				controlSolutionDialogueEntry.getValue().sendMessage(jsonString);
			} catch (IOException e) {

			}
		}
	}

	/**
	 * 获取当前时间的string类型
	 * 
	 * @return
	 */
	public static String getNowDateTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
		return "'" + sdf.format(new Date()) + "'";
	}

}
